/****************************************************************************
 * Copyright (C) 2009-2010 SciTouch LLC
 * 
 * This file is part of Indigo toolkit.
 * 
 * This file may be distributed and/or modified under the terms of the
 * GNU General Public License version 3 as published by the Free Software
 * Foundation and appearing in the file LICENSE.GPL included in the
 * packaging of this file.
 * 
 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 ***************************************************************************/

#ifndef __molecule_fingerprint__
#define __molecule_fingerprint__

#include "base_cpp/tlscont.h"
#include "molecule/molecule.h"
#include "molecule/molecule_arom.h"

class TautomerSuperStructure;

// Fingerprint consists of 5 parts: EXT + ORD + ANY + TAU + SIM.
// EXT is always 3 bytes long, other parts' sizes are configured.
// ORD, ANY, and SIM parts are build up from fragments.
// Each fragments goes to:
//    SIM -- as long as it has no query atoms/bonds and is small enough
//    ORD -- as long as it has no query atoms/bonds
//    ANY (with bond types discarded) -- as long as it has no query atoms
//    ANY (with atom types discarded) -- as long as it has no query bonds
//    ANY (with atom and bond types discarded) -- always
// TAU part is build up from a 'supermolecule' having some added bonds,
//     and with all bond types discarded
// EXT part is build up from some element, isotope, and charge counters

struct MoleculeFingerprintParameters
{
   int ord_qwords, any_qwords, tau_qwords, sim_qwords;

   int fingerprintSize       () const { return 3 + (ord_qwords + any_qwords + tau_qwords + sim_qwords) * 8; }
   int fingerprintSizeExt    () const { return 3; }
   int fingerprintSizeExtOrd () const { return 3 + ord_qwords * 8;}
   int fingerprintSizeOrd    () const { return ord_qwords * 8; }
   int fingerprintSizeAny    () const { return any_qwords * 8; }
   int fingerprintSizeTau    () const { return tau_qwords * 8; }
   int fingerprintSizeSim    () const { return sim_qwords * 8; }

   int fingerprintOffsetSim  () const { return 3 + (ord_qwords + any_qwords + tau_qwords) * 8; }
};

class MoleculeFingerprintBuilder
{
public:
   MoleculeFingerprintBuilder (const Molecule &mol, const MoleculeFingerprintParameters &parameters);

   bool query;

   bool skip_ord; // don't build 'ordinary' part of the fingerprint
   bool skip_sim; // don't build 'similarity' part of the fingerprint
   bool skip_tau; // don't build 'tautomer' part of the fingerprint
   bool skip_ext; // don't build 'extra' part of the fingerprint
   bool skip_ext_charge; // don't store information about charges in 'extra' part

   bool skip_any_atoms; // don't build 'any atoms' part of the fingerprint
   bool skip_any_bonds; // don't build 'any bonds' part of the fingerprint
   bool skip_any_atoms_bonds; // don't build 'any atoms, any bonds' part of the fingerprint

   void process ();

   const byte * get ();
   byte * getSim ();
   byte * getOrd ();
   byte * getAny ();
   byte * getTau ();
   
   int countBits_Sim ();

   void (*cb_fragment) (const Molecule &mol, const Array<int> &vertices, const Array<int> &edges,
                        bool use_atoms, bool use_bonds, dword hash);

   DEF_ERROR("fingerprint builder");
protected:

   static void _handleTree     (const Graph &graph, const int *v_mapping, const int *e_mapping, void *context);
   static bool _handleCycle    (const Graph &graph, const Array<int> &vertices, const Array<int> &edges, void *context);

   static int _vertex_code (const Graph &graph, int vertex_idx, const void *context);
   static int _edge_code (const Graph &graph, int edge_idx, const void *context);

   static int _maximalSubgraphCriteriaValue (const Graph &graph, const int *v_mapping, 
      const int *e_mapping, void *context);

   void _handleSubgraph (const Graph &graph, const Array<int> &vertices, const Array<int> &edges);

   dword _canonicalizeFragment (const Molecule &mol, const Array<int> &vertices, const Array<int> &edges, 
      bool use_atoms, bool use_bonds);

   void _canonicalizeFragmentAndSetBits (const Molecule &mol, const Array<int> &vertices, const Array<int> &edges, 
      bool use_atoms, bool use_bonds, int subgraph_type, dword &bits_to_set);

   void _makeFingerprint (const Molecule &mol);
   void _calcExtraBits (const Molecule &mol);

   void _setTauBits (const char *str, int nbits);
   void _setOrdBits (const char *str, int nbits);

   static void _setBits (dword hash, byte *fp, int size, int nbits);
   
   const Molecule &_mol;
   const MoleculeFingerprintParameters &_parameters;

   bool _canAtomBeHydrogen (const Molecule &mol, int atom_index);

   // these parameters are indirectly passed to the callbacks
   TautomerSuperStructure *_tau_super_structure;
   bool _is_cycle;

   TL_CP_DECL(Array<byte>, _total_fingerprint);

private:
   MoleculeFingerprintBuilder (const MoleculeFingerprintBuilder &); // no implicit copy
};

#endif
